/*****************************************************************************

                 ,//////,   ,////    ,///' /////,
                ///' ./// ///'///  ///,    ,, //
               ///////,  ///,///   '/// //;''//,
             ,///' '///,'/////',/////'  /////'/;,

    Copyright 2010 Marcus Jansson <mjansson256@yahoo.se>

    This file is part of ROSA - Realtime Operating System for AVR32.

    ROSA is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ROSA is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ROSA.  If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
/* Tab size: 4 */

#include "rosa_config.h"
#include "drivers/delay.h"
#include "API/rosa.h"
#include "kernel/rosa_int.h"
#include "kernel/rosa_systick.h"
#include "drivers/led.h"
#include "kernel/rosa_scheduler.h"
#include "kernel/rosa_ext.h"

// Global system tick variable.
static unsigned int sysTickCount;
/// Returns current tick value
timerType ROSA_getCurrentTick(void)
{
	return sysTickCount;
}

/// Stops the systick interrupt
void stopSysTick(void)
{
	volatile avr32_tc_t * tc = &AVR32_TC;
	tc->channel[0].idr = (1 << 4);
}

/// Starts the systick interrupt
void startSysTick(void)
{
	volatile avr32_tc_t * tc = &AVR32_TC;
	tc->channel[0].ier = (1 << 4);
}

/// Returns true if the systick interrupt is enabled
bool systickIsRunning(void)
{
	volatile avr32_tc_t * tc = &AVR32_TC;
	return tc->channel[0].imr & (1 << 4);
}

#include <stdlib.h>
#include "kernel/rosa_queues.h"

#define SYSTICK_SAMPLE_COUNT 128
static unsigned short startTimes[SYSTICK_SAMPLE_COUNT];
/*static unsigned short middleTimes[SYSTICK_SAMPLE_COUNT];
static unsigned short foo[SYSTICK_SAMPLE_COUNT];
static unsigned short bar[SYSTICK_SAMPLE_COUNT];*/
static unsigned short stopTimes[SYSTICK_SAMPLE_COUNT];
unsigned char systickTimesWriteIndex;

/***********************************************************
 * timerInterruptHandler
 *
 * Comment:
 * 	This is the basic timer interrupt service routine.
 **********************************************************/
__attribute__((__interrupt__))
void sysTickInterrupt(void)
{
	volatile avr32_tc_t * tc = &AVR32_TC;
	
	startTimes[systickTimesWriteIndex] = tc->channel[0].cv;
	sysTickCount++;

	//Read the timer status register to clear the interrupt flag
	int __attribute__((unused)) sr = tc->channel[0].sr;
	
	//Iterate in the waiting list to see if any task needs to wake up
	if(waitingQueueHead != NULL)
	{
		while ( waitingQueueHead->wakeUpTime == sysTickCount)
		{
			if(waitingQueueHead->semStatus == SOS_FiniteWait)
				timeoutSemaphoreOperation(waitingQueueHead);
			
			// Wake up tasks when their waiting is over.
			rosa_moveFirstTaskFromWaitingToReady();
		}
	}
	if ( readyQueueHead && (readyQueueHead->dynamicPriority > EXECTASK->dynamicPriority))
	{
		// Save currently executing task's state
		contextSaveFromISR();
		rosa_addToReadyQueue(EXECTASK);
		EXECTASK->state = Ready;
				
		// Restore higher-priority task
		EXECTASK = rosa_popHighestPriorityTaskFromReadyQueue();
		EXECTASK->state = Running;
		contextRestoreFromISR();
	}
	stopTimes[systickTimesWriteIndex] = tc->channel[0].cv;
	systickTimesWriteIndex = (systickTimesWriteIndex + 1) % SYSTICK_SAMPLE_COUNT;
}

/***********************************************************
 * timerPeriodSet
 *
 * Comment:
 * 	Set the timer period to 'ms' milliseconds.
 *
 **********************************************************/
int timerPeriodSet(unsigned int ms)
{

	int rc, prescale;
	int f[] = { 2, 8, 32, 128 };
	//FOSC0 / factor_prescale * time[s];
	prescale = AVR32_TC_CMR0_TCCLKS_TIMER_CLOCK2;
	rc = FOSC0 / f[prescale - 1] * ms / 1000;
	timerPrescaleSet(prescale);
	timerRCSet(rc);
	return rc * prescale / FOSC0;
}

/// Returns the total amount of time (in periods of the TC0 6 MHz clock)
unsigned int getTotalSystickTime(void)
{
	unsigned int sum = 0;
	for(int i=0; i<SYSTICK_SAMPLE_COUNT; i++)
	{
		sum += stopTimes[i] - startTimes[i];
	}
	return sum;
}
